import numpy
from tempfile import mkstemp
from os       import close
from copy     import deepcopy
from .functions import parse_indices, subspace_array
from .constants import CONSTANTS

# ====================================================================
#
# FileArray object
#
# ====================================================================

class FileArray(object):
    ''' 

A indexable N-dimensional array supporting masked values.

The array is stored on disk in a temporary file until it is
accessed. The directory containing the temporary file may be found and
set in the `cf.CONSTANTS` dictionary.

**Indexing**

The array is indexable in a similar way to numpy array indexing but
for two important differences:

* Size 1 dimensions are never removed.

  An integer index i takes the i-th element but does not reduce the
  rank of the output array by one.

* When advanced indexing is used on more than one dimension, the
  advanced indices work independently.

  When more than one dimension's slice is a 1-d boolean array or 1-d
  sequence of integers, then these indices work independently along
  each dimension (similar to the way vector subscripts work in
  Fortran), rather than by their elements.

**Examples**

>>> f.shape
(12, 19, 73, 96)
>>> d[0, :, [0,1], [0,1,2]].shape
(1, 19, 2, 3)

'''
    __slots__ = ('_masked_as_record',
                 '_partition_file',
                 'dtype',
                 'size',
                 'ndim',
                 'shape',
                 )

    def __init__(self, array):
        '''

**Initialization**

:Parameters:

    array : numpy array
        The array to be stored on disk in a temporary file.        

**Examples**

>>> f = FileArray(numpy.array([1, 2, 3, 4, 5]))
>>> f = FileArray(numpy.ma.array([1, 2, 3, 4, 5]))

'''
        # ------------------------------------------------------------
        # Use mkstemp because we want to be responsible for deleting
        # the temporary file when done with it.
        # ------------------------------------------------------------
        fd, _partition_file = mkstemp(prefix='cf_array_', suffix='.npy', 
                                      dir=CONSTANTS['TEMPDIR'])

        close(fd)

        self._partition_file = _partition_file
        '''

The name of the temporary file storing the array.

**Examples**

>>> f._partition_file
'/tmp/cf_array_B8SSw2.npy'

'''

        self.dtype = array.dtype
        '''

Numpy data type of the array.

**Examples**

>>> f.dtype
dtype('float64')

'''

        self.shape = array.shape
        '''

Tuple of the array's dimension sizes.

**Examples**

>>> f.shape
(73, 96)

'''

        self.size = array.size
        '''
Number of elements in the array.

**Examples**

>>> f.shape
(73, 96)
>>> f.size
7008

'''

        self.ndim = array.ndim
        '''

Number of dimensions in the array.

**Examples**

>>> f.shape
(73, 96)
>>> f.ndim
2

'''

        if numpy.ma.is_masked(array):
            # Array is a masked array. Save it as record array with
            # 'data' and 'mask' elements because this seems much
            # faster than using numpy.ma.dump.
            self._masked_as_record = True
            numpy.save(_partition_file, array.toflex())
        else:
            self._masked_as_record = False
            if hasattr(array, 'mask'):
                # Array is a masked array with no masked elements
                numpy.save(_partition_file, array.view(numpy.ndarray))
            else:
                # Array is not a masked array.
                numpy.save(_partition_file, array)
    #--- End: def

    def __str__(self):
        '''
x.__str__() <==> str(x)

'''
        return '%s: %s' % (self.__class__.__name__, {'dtype' : self.dtype,
                                                     'shape' : self.shape,
                                                     'ndim'  : self.ndim,
                                                     'size'  : self.size,
                                                     })
    #--- End: def

    def __getitem__(self, indices):
        '''
x.__getitem__(indices) <==> x[indices]

'''
        array = numpy.load(self._partition_file)

        indices = parse_indices(array, indices)

        array = subspace_array(array, indices)

        if self._masked_as_record:
            # Convert a record array to a masked array
            array = numpy.ma.array(array['_data'], mask=array['_mask'],
                                   copy=False)
            array.shrink_mask()
        #--- End: if

        # Return the array
        return array
    #--- End: def

    def close(self):
        '''

Close all referenced open files.

:Returns:

    None

**Examples**

>>> a.close()

'''     
        # An instance references no open files
        pass
    #--- End: def
   
    def copy(self):
        '''

Return a deep copy.

Equivalent to ``copy.deepcopy(f)``.

:Returns:

    out :
        A deep copy.
    
**Examples**

>>> f.copy()

'''  
        return deepcopy(self)
    #--- End: def

#--- End: class
